package com.fitzframework.util;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

@Slf4j
public class ClassUtil {

    public static final String FILE_PROTOCOL = "file";
    public static final String CLASS_FILE = ".class";

    /**
     * 扫描包下所有的class
     * @param packageName
     * @return
     */
    public static Set<Class<?>> extractPackageClass(String packageName){
        //1.获取类加载器,获取项目发布的实际路径
        ClassLoader classLoader = getClassLoader();
        //2.通过类加载器获取到加载的资源
        URL url = classLoader.getResource(packageName.replace(".","/"));
        if (url == null){
            log.warn("无法从package中获取到任何信息:{}",packageName);
            return null;
        }
        //3.依据不同的资源类型，采用不同的方式获取资源的集合
        Set<Class<?>> classSet = null;
        if (url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
            classSet = new HashSet<>();
            //获取到绝对路径
            File packageDir = new File(url.getPath());
            extractClassFile(classSet,packageDir,packageName);
        }
        //还可以处理其他协议类型的
        return classSet;
    }

    /**
     * 递归获取package下的所有的class文件
     * @param emptyClassSet
     * @param fileSource
     * @param packageName
     */
    private static void extractClassFile(Set<Class<?>> emptyClassSet, File fileSource, String packageName) {
        //如果是文件 就停止
        if (fileSource.isFile()){
            return;
        }
        File[] files = fileSource.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                if (file.isDirectory()){
                    return true;
                }else {
                    String absoluteFilePath = file.getAbsolutePath();
                    if (absoluteFilePath.endsWith(CLASS_FILE)){
                        addToClassSet(absoluteFilePath);
                    }
                }
                return false;
            }

            private void addToClassSet(String absoluteFilePath) {
                //1.从class的绝对路径中取出包含了packageName的className
                absoluteFilePath = absoluteFilePath.replace(File.separator,".");
                String className = absoluteFilePath.substring(absoluteFilePath.indexOf(packageName));
                className = className.substring(0,className.lastIndexOf("."));

                //2.通过反射机制获取到Class对象放入classSet中
                Class<?> targetClass = loadClass(className);
                emptyClassSet.add(targetClass);

            }
        });
        if (files != null){
            for (File childSource : files){
                extractClassFile(emptyClassSet, childSource, packageName);
            }
        }
    }

    /**
     * 获取class对象
     * @param className
     * @return
     */
    public static Class<?> loadClass(String className){
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error("load class error:",e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建class实例
     * @param clazz
     * @param <T>
     * @param accessible
     * @return
     */
    public static <T> T newInstance(Class<?> clazz,boolean accessible){
        try {
            Constructor constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(accessible);
            return (T)constructor.newInstance();
        } catch (Exception e) {
            log.error("创建class对象实例异常:",e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 给属性字段设置值
     * @param field
     * @param target
     * @param value
     * @param accessible
     */
    public static void setField(Field field,Object target,Object value,boolean accessible){
        field.setAccessible(accessible);
        try {
            field.set(target,value);
        } catch (IllegalAccessException e) {
            log.error("设置属性失败:",e);
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        extractPackageClass("org.fitz.entity");
    }

    /**
     * 获取类加载器
     * @return
     */
    public static ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }
}
